<template>
  <div class="simple-graph-vis-container">
    <div :id="querySelectId" class="simple-graph-vis"></div>
  </div>
</template>

<script lang="ts">
import { Vue, Prop, Component, Watch } from 'vue-property-decorator'
import G6 from '@antv/g6'
import { debounce } from 'lodash'

const fittingString = (input: string, maxWidth: any, fontSize: any) => {
  const ellipsis = '...'
  const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]
  let currentWidth = 0
  let result = input
  const pattern = new RegExp('[\u4E00-\u9FA5]+') // distinguish the Chinese charactors and letters
  input.split('').forEach((letter: string, i) => {
    if (currentWidth > maxWidth - ellipsisLength) {
      return
    }
    if (pattern.test(letter)) {
      // Chinese charactors
      currentWidth += fontSize
    } else {
      // get the width of single letter according to the fontSize
      currentWidth += G6.Util.getLetterWidth(letter, fontSize)
    }
    if (currentWidth > maxWidth - ellipsisLength) {
      result = `${input.slice(0, i)}${ellipsis}`
    }
  })
  return result
}

@Component({
  components: {},
})
export default class SimpleKGraphVis extends Vue {
  @Prop() containerWidth: any
  @Prop() rawData: any
  @Prop() querySelectId: any
  private highlightNodeId: any

  public graph: any | null = null
  public graphData: any | null = null

  mounted() {
    this.initGraph()
  }

  @Watch('rawData', { immediate: true, deep: true })
  renderGraph(newValue: any) {
    setTimeout(() => {
      if (newValue) {
        if (newValue.edges !== undefined && this.graph) {
          this.graphData = this.preprocessData(newValue)
          this.graph.data(this.graphData)
          this.graph.render()
          this.highlightNode()
          this.graph.setMode('default')
        }
      }
    }, 300)
  }

  @Watch('containerWidth')
  public graphVisWidthChange() {
    this.changeGraphsize()
  }

  public changeGraphsize = debounce(() => {
    this._changeGraphsize()
  }, 500)

  public _changeGraphsize() {
    this.graph.changeSize(this.containerWidth, this.graph.cfg.height)
  }

  public highlightNode() {
    this.highlightNodeId = this.graphData.nodes[0].id.toString()
    if (this.highlightNodeId !== -1) {
      const item = this.graph.findById(this.highlightNodeId)
      this.graph.updateItem(item, {
        style: {
          stroke: '#ff3300',
        },
      })
    }
  }

  // 预处理后端数据成前端所需格式
  public preprocessData(rawData: any) {
    const data = JSON.parse(JSON.stringify(rawData))
    data.nodes = data.vertices
    data.nodes.forEach((item: any) => {
      item.id = item.id.toString()
      item.collapse = false
    })
    data.edges.forEach((item: any) => {
      item.source = item.source.toString()
      item.target = item.target.toString()
    })
    return data
  }

  public initGraph() {
    // 自定义节点
    G6.registerNode(
      'simple-node-card',
      {
        drawShape(cfg: any, group: any) {
          const strokeColorMap: any = {
            '-1': '#78C06E',
            '0': '#6E80A2',
            '1': '#40A9FF',
          }
          const r = 2
          const shape = group.addShape('rect', {
            attrs: {
              x: 0,
              y: 0,
              width: 100,
              height: 35,
              stroke: strokeColorMap[String(cfg.isClass)],
              lineWidth: 2,
              fill: 'white',
              radius: r,
            },
            name: 'main-box',
            draggable: true,
          })

          // name text
          group.addShape('text', {
            attrs: {
              textAlign: 'center',
              textBaseline: 'middle',
              y: 20,
              x: 50,
              lineHeight: 20,
              fontSize: 14,
              text: fittingString(cfg.label, 90, 14),
              fill: 'rgba(0,0,0, 1)',
            },
            name: 'label',
            draggable: true,
          })

          if (cfg.isClass === -1) {
            group.addShape('image', {
              attrs: {
                x: 5,
                y: 12,
                width: 16,
                height: 16,
                img: require('@/assets/gis-icon.svg'),
              },
              name: 'gis-icon',
              draggable: true,
            })
          }
          return shape
        },
      },
      'single-node'
    )

    const tooltip = new G6.Tooltip({
      // the types of items that allow the tooltip show up
      // 允许出现 tooltip 的 item 类型
      itemTypes: ['edge', 'node'],
      // custom the tooltip's content
      // 自定义 tooltip 内容
      getContent: (event: any) => {
        const outDiv = document.createElement('div')
        outDiv.style.width = 'fit-content'
        // outDiv.style.padding = '0px 0px 20px 0px';
        outDiv.innerHTML = `
      <div>${event.item.getModel().label || event.item.getModel().id}
      </div>`
        return outDiv
      },
    })

    const container: any = document.querySelector(`#${this.querySelectId}`)
    const width = container.scrollWidth
    const height = container.scrollHeight || 160

    this.graph = new G6.Graph({
      container,
      width,
      height,
      layout: {
        type: 'dagre',
        rankdir: 'LR',
        align: 'UL',
        nodesep: 10,
        ranksep: 40,
        controlPoints: true,
      },
      plugins: [tooltip],
      defaultNode: {
        type: 'simple-node-card',
        labelCfg: {
          style: {
            opacity: 0,
          },
        },
      },
      defaultEdge: {
        type: 'line',
        size: 1,
        color: '#e2e2e2',
        style: {
          endArrow: {
            path: 'M 0,0 L 8,4 L 8,-4 Z',
            fill: '#e2e2e2',
          },
          radius: 20,
          lineAppendWidth: 2,
        },
        labelCfg: {
          style: {
            opacity: 0,
          },
        },
      },
      nodeStateStyles: {
        selected: {
          lineWidth: 3,
          stroke: '#ff3300',
        },
      },
      edgeStateStyles: {
        hover: {
          'text-shape': {
            opacity: 1,
          },
        },
      },
      modes: {
        default: [
          'zoom-canvas',
          { type: 'click-select', multiple: false },
          'drag-canvas',
          'drag-node',
        ], // 默认
      },
      fitView: true,
      // animate: true,
    })

    this.graph.on('nodeselectchange', (event: any) => {
      if (event.selectedItems.nodes[0]?._cfg) {
        const selectedNodeModel = event.selectedItems.nodes[0]._cfg.model
        this.$emit('on-node-select-change', {
          id: selectedNodeModel.id,
          label: selectedNodeModel.label,
        })
      }
    })

    this.graph.setMinZoom(0.001)

    // 右键双击画布
    this.graph.on('canvas:dblclick', () => {
      this.graph.fitView(20)
    })

    if (typeof window !== 'undefined') {
      window.addEventListener('onresize', () => {
        if (!this.graph || this.graph.get('destroyed')) {
          return
        }
        if (!container || !container.scrollWidth || !container.scrollHeight) {
          return
        }
        this.graph.changeSize(container.scrollWidth, container.scrollHeight)
      })
    }
  }
}
</script>

<style lang="less" scoped>
.simple-graph-vis-container {
  position: relative;
  width: 100%;

  .simple-graph-vis {
    height: 100%;
    width: 100%;
  }
}
</style>
